#!/usr/bin/env python
#
# Copyright 2011 Google Inc.
# Copyright 2011 Joe LaPenna
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

"""Main entrypoint for a checkins mashup."""

# Third party libraries
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib'))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'private'))
sys.path.insert(0,
    os.path.join(os.path.dirname(__file__), 'lib/appengine_utilities'))

# Settings
import settings

# Standard python modules
import datetime
import logging
import os
import random
import sys
import time

# Other libs
import feedparser
from pshb import pshb

# Appengine sdk
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template

# Appengine provided third party libs
from django.utils import simplejson as json


DEBUG = settings.DEBUG
CHECKINS_SUBSCRIBER_KEY_NAME = settings.CHECKINS_SUBSCRIBER_KEY_NAME


# Paths
SUBSCRIBE_CHECKINS_TASK_PATH = '/task/subscribe_checkins'
CHECKINS_CALLBACK_PATH = '/hubbub/checkins'

# UI Paths
ROOT_PATH = '/'
MAP_PATH = '/map'
CHECKIN_STREAM_PATH = '/checkins'


# Models

class Checkin(db.Model):

  # entry.author
  author = db.StringProperty()

  # entry.author_detail.href
  author_url = db.StringProperty()

  # entry.link
  link = db.StringProperty()

  # entry.poco_address
  address = db.StringProperty()

  # entry.poco_formatted
  address_formatted = db.StringProperty()

  # entry.published_parsed
  published = db.DateTimeProperty()

  # entry.georss_featurename
  feature_name = db.StringProperty()

  # entry.georss_point
  point = db.GeoPtProperty()


# Handlers

class MapHandler(webapp.RequestHandler):

  def get(self):
    context = {}
    self.response.out.write(template.render('templates/map.html', context))


class CheckinStreamHandler(webapp.RequestHandler):

  def get(self):
    query = db.Query(Checkin).order('-published')
    checkins = query.fetch(30)

    # Handle an empty DB with fake data.
    if (len(checkins) == 0):
      base_id = int(time.time()) - 4
      checkins = [];
      for i in range(13):
        checkins.append(
            Checkin(key_name=str(base_id),
                link=str(base_id),
                address_formatted='Address %s' % base_id,
                point=db.GeoPt(random.randint(0, 90), random.randint(0, 90)),
                published=datetime.datetime.fromtimestamp(base_id)))
        base_id -= 1;

    checkins_as_dicts = []
    for checkin in checkins:
      checkins_as_dicts.append({
          'author': checkin.author,
          'author_url': checkin.author_url,
          'link': checkin.link,
          'address': checkin.address,
          'address_formatted': checkin.address_formatted,
          'published': time.mktime(checkin.published.timetuple()),
          'feature_name': checkin.feature_name,
          'point': (checkin.point.lat, checkin.point.lon),
      })
    json.dump(checkins_as_dicts, self.response.out)


class SubscribeCheckinsTask(pshb.SubscribeTask):
  """Subscribe specifically to public checkins."""

  CHECKINS_SEARCH = (
      'https://www.googleapis.com/buzz/v1/activities/track?q=source:latitude')

  def post(self):
    """Subscribe to the checkins feed."""
    logging.debug('SubscribeCheckinsTask: POST')
    url = SubscribeCheckinsTask.CHECKINS_SEARCH
    callback = CHECKINS_CALLBACK_PATH
    subscriber = CHECKINS_SUBSCRIBER_KEY_NAME
    self.subscribe(url, callback, subscriber)


class CheckinsCallbackHandler(pshb.CallbackHandler):
  """Stores checkins when the hub recieves a post."""

  def post(self):
    feed = feedparser.parse(self.request.body)
    checkins = []
    for entry in feed.entries:
      try:
        checkin = Checkin(
            key_name=entry.id,
            author=entry.author,
            author_url=entry.author_detail.href,
            link=entry.link,
            address=entry.poco_address,
            address_formatted=entry.poco_formatted,
            published=datetime.datetime.fromtimestamp(
                time.mktime(entry.published_parsed)),
            feature_name=entry.georss_featurename,
            point=db.GeoPt(*entry.georss_point.split(' '))
            )
        checkins.append(checkin)
      except AttributeError, e:
        logging.debug('Failed to add checkin: %s', entry.id);

    # Store the checkins.
    db.put(checkins)


# Main and stuff


def create_application():
  """Create the application that will be run in main."""
  application = webapp.WSGIApplication(
      [

        # PSHB Callbacks
        (CHECKINS_CALLBACK_PATH, CheckinsCallbackHandler),

        # UI
        (ROOT_PATH, MapHandler),
        (MAP_PATH, MapHandler),
        (CHECKIN_STREAM_PATH, CheckinStreamHandler),

        # PSHB Library
        (pshb.SUBSCRIBE_PATH, pshb.SubscribeTask),
        (pshb.CALLBACK_PATH, pshb.CallbackHandler),

        # Admin Tasks
        (SUBSCRIBE_CHECKINS_TASK_PATH, SubscribeCheckinsTask),

      ],
      debug=DEBUG)
  return application


# -- Main entry point (two methods for profiling). -- #


def profile_main():
  """From http://code.google.com/appengine/kb/commontasks.html"""
  logging.debug('DEBUG Main Being Used')
  # This is the main function for profiling
  # We've renamed our original main() to real_main()
  import cProfile, pstats
  prof = cProfile.Profile()
  prof = prof.runctx("real_main()", globals(), locals())
  print "<div class=\"debug\"><pre>"
  stats = pstats.Stats(prof)
  stats.sort_stats("time")  # Or cumulative
  stats.print_stats(80)  # 80 = how many to print
  # The rest is optional.
  # stats.print_callees()
  # stats.print_callers()
  print "</pre></div>"


def real_main():
  """Hide the real main method so that we can run profiling."""
  run_wsgi_app(create_application())



def set_main():
  """Set __module__.main to either the real or debugging main.

  Uses global "DEBUG"
  """
  global main
  if DEBUG:
    main = profile_main
  else:
    main = real_main


# this needs to be executed at the module level or main() will not be cached by
# GAE.
set_main()


if __name__ == '__main__':
  main()
